/*
 * Copyright © 2002-2011 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

using System.Reflection;
using System.Reflection.Emit;
using System.Collections;
using System.Runtime.InteropServices;
using NUnit.Framework;
using Spring.Objects;
using Spring.Util;

namespace Spring.Proxy;

/// <summary>
/// A base set of unit tests for the various AbstractProxyTypeBuilder subclasses.
/// </summary>
/// <author>Rick Evans</author>
/// <author>Bruno Baia</author>
public abstract class AbstractProxyTypeBuilderTests
{
    [Test]
    public void GeneratesProxyNameIfNoneExplicitlySupplied()
    {
        IProxyTypeBuilder builder = GetProxyBuilder();
        Assert.IsNotNull(builder.Name);
    }

    [Test]
    public void DoesNotGenerateProxyNameIfOneIsExplicitlySupplied()
    {
        IProxyTypeBuilder builder = GetProxyBuilder();
        builder.Name = "Bing";
        Assert.AreEqual("Bing", builder.Name);
    }

    [Test]
    public void AppliesAttributeToType()
    {
        IProxyTypeBuilder builder = GetProxyBuilder();
        builder.TargetType = typeof(MarkerClass);
        builder.TypeAttributes = new Attribute[] { new MarkerAttribute() };

        Type proxy = builder.BuildProxyType();
        Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");

        object[] atts = proxy.GetCustomAttributes(false);
        Assert.IsNotNull(atts, "Should have had 1 custom attribute applied to the generated proxy type.");
        Assert.AreEqual(1, atts.Length, "Should have had 1 custom attribute applied to the generated proxy type.");
        Assert.AreEqual(typeof(MarkerAttribute), atts[0].GetType(), "Wrong System.Type of Attribute applied to the generated proxy type.");
    }

    [Test]
    public void AppliesStarredMemberAttributesToAllMethods()
    {
        IProxyTypeBuilder builder = GetProxyBuilder();
        builder.TargetType = typeof(TargetObjectTest);
        IDictionary atts = new Hashtable();
        string methodPrefix = typeof(ITargetObjectTest).FullName + ".";
        atts.Add("*", new Attribute[] { new MarkerAttribute() });
        builder.MemberAttributes = atts;
        Type proxy = builder.BuildProxyType();
        object foo = Activator.CreateInstance(proxy);

        Type fooType = foo.GetType();
        MethodInfo method = ReflectionUtils.GetMethod(fooType, methodPrefix + "InterfaceMethodWithArguments", new Type[] { typeof(string) });
        object[] appliedAttributes = method.GetCustomAttributes(false);
        Assert.AreEqual(1, appliedAttributes.Length,
            "Custom attribute not applied to member method.");
        method = ReflectionUtils.GetMethod(fooType, methodPrefix + "InterfaceVirtualMethodWithNoArguments", Type.EmptyTypes);
        appliedAttributes = method.GetCustomAttributes(false);
        Assert.AreEqual(1, appliedAttributes.Length,
            "Custom attribute not applied to member method.");
    }

    [Test]
    public void AppliesSpecificMemberAttributesToSpecificMethodOnly()
    {
        IProxyTypeBuilder builder = GetProxyBuilder();
        builder.TargetType = typeof(TargetObjectTest);
        IDictionary atts = new Hashtable();
        atts.Add("InterfaceVirtualMethodWithNoArguments", new Attribute[] { new MarkerAttribute() });
        builder.MemberAttributes = atts;

        Type proxy = builder.BuildProxyType();
        object foo = Activator.CreateInstance(proxy);

        Type fooType = foo.GetType();
        MethodInfo method = ReflectionUtils.GetMethod(fooType, "InterfaceMethodWithArguments", new Type[] { typeof(string) });
        if (method == null)
        {
            method = ReflectionUtils.GetMethod(fooType, "Spring.Proxy.ITargetObjectTest.InterfaceMethodWithArguments", new Type[] { typeof(string) });
        }

        object[] appliedAttributes = method.GetCustomAttributes(false);
        Assert.AreEqual(0, appliedAttributes.Length,
            "Custom attribute (erroneously) applied to member method.");
        method = ReflectionUtils.GetMethod(fooType, "InterfaceVirtualMethodWithNoArguments", Type.EmptyTypes);
        if (method == null)
        {
            method = ReflectionUtils.GetMethod(fooType, "Spring.Proxy.ITargetObjectTest.InterfaceVirtualMethodWithNoArguments", Type.EmptyTypes);
        }

        appliedAttributes = method.GetCustomAttributes(false);
        Assert.AreEqual(1, appliedAttributes.Length,
            "Custom attribute not applied to member method.");
    }

    [Test]
    // SPRNET-1262
    public void AppliesSpecificTypeAttributeWithNoPropertySettersToTargetTypeUsingCustomAttributeBuilder()
    {
        ConstructorInfo ci = typeof(FakeServiceKnownTypeAttribute).GetConstructor(new Type[] { typeof(Type) });
        CustomAttributeBuilder cab = new CustomAttributeBuilder(ci, new object[] { typeof(TestObject) });

        IProxyTypeBuilder builder = GetProxyBuilder();
        builder.TargetType = typeof(ClassWithFakeServiceKnownTypeAttribute);
        builder.TypeAttributes.Add(cab);

        Type proxy = builder.BuildProxyType();
        Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");

        object[] attrs = proxy.GetCustomAttributes(false);
        Assert.IsNotNull(attrs, "Should have had 1 attribute applied to the target type.");
        Assert.AreEqual(1, attrs.Length, "Should have had 1 attribute applied to the target type.");
        Assert.AreEqual(typeof(FakeServiceKnownTypeAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the target type.");

        FakeServiceKnownTypeAttribute fgsta = attrs[0] as FakeServiceKnownTypeAttribute;
        Assert.AreEqual(typeof(TestObject), fgsta.Type, "Property 'Type' on Target Attribute not set!");
    }

    [Test]
    public void ImplementsInterfaceHierarchy()
    {
        IProxyTypeBuilder builder = GetProxyBuilder();
        builder.TargetType = typeof(MultipleInterfaces);

        Type proxy = builder.BuildProxyType();
        Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");

        object foo = Activator.CreateInstance(proxy);
        Assert.IsTrue(foo is IBase);
        Assert.IsTrue(foo is IInherited);

        // try to call proxied interface methods
        ((IBase) foo).Base();
        ((IInherited) foo).Base();
        ((IInherited) foo).Inherited();
    }

    [Test]
    public void ProxySpecificTargetTypeAttributeWithReadOnlyProperty()
    {
        IProxyTypeBuilder builder = GetProxyBuilder();
        builder.TargetType = typeof(ClassWithGuidAttribute);

        Type proxy = builder.BuildProxyType();
        Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");

        object[] attrs = proxy.GetCustomAttributes(false);
        Assert.IsNotNull(attrs, "Should have had 1 attribute applied to the target type.");
        Assert.AreEqual(1, attrs.Length, "Should have had 1 attribute applied to the target type.");
        Assert.AreEqual(typeof(GuidAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the target type.");

        GuidAttribute ga = attrs[0] as GuidAttribute;
        Assert.AreEqual("7cfc9607-e81a-48ac-a080-bda88193607b", ga.Value);
    }

    [Test]
    public void ProxySpecificTargetTypeAttributeWithPropertySetterChangingDefaultBehavior()
    {
        IProxyTypeBuilder builder = GetProxyBuilder();
        builder.TargetType = typeof(ClassWithFakeXmlElementAttribute);
        Type proxy = builder.BuildProxyType();
        Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");
        MethodInfo method = proxy.GetMethod("Spring.Proxy.ISomeMarkerInterface.MarkerMethod", BindingFlags.NonPublic | BindingFlags.Instance);
        if (method == null)
        {
            method = proxy.GetMethod("MarkerMethod");
        }

        Assert.IsNotNull(method);
        object[] attrs = method.GetParameters()[0].GetCustomAttributes(false);
        Assert.IsNotNull(method);
        Assert.IsNotNull(attrs, "Should have 1 attribute applied to the target method.");
        Assert.AreEqual(1, attrs.Length, "Should have 1 attribute applied to the target method.");
        Assert.AreEqual(typeof(FakeXmlElementAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the target method.");

        FakeXmlElementAttribute xea = attrs[0] as FakeXmlElementAttribute;
        Assert.AreEqual(-1, xea.Order);
    }

    [Test]
    public void ProxySpecificTargetTypeAttributeNotInstantiableWithDefaultValues()
    {
        IProxyTypeBuilder builder = GetProxyBuilder();
        builder.TargetType = typeof(ClassWithFakeGenerateScriptTypeAttribute);

        Type proxy = builder.BuildProxyType();
        Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");

        object[] attrs = proxy.GetCustomAttributes(false);
        Assert.IsNotNull(attrs, "Should have had 1 attribute applied to the target type.");
        Assert.AreEqual(1, attrs.Length, "Should have had 1 attribute applied to the target type.");
        Assert.AreEqual(typeof(FakeGenerateScriptTypeAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the target type.");

        FakeGenerateScriptTypeAttribute fgsta = attrs[0] as FakeGenerateScriptTypeAttribute;
        Assert.AreEqual("ScriptName", fgsta.Name);
    }

    // http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=296032
    // should not fail
    // Not fixed by .NET 2.0 SP1.
    [Test]
    public void ProxySpecificTargetTypeAttributeWithPublicEnumProperty()
    {
        IProxyTypeBuilder builder = GetProxyBuilder();
        builder.TargetType = typeof(ClassWithPublicEnumPropertyAttribute);
        Type proxy = builder.BuildProxyType();
        Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");
        MethodInfo method = proxy.GetMethod("Spring.Proxy.ISomeMarkerInterface.MarkerMethod", BindingFlags.NonPublic | BindingFlags.Instance);
        if (method == null)
        {
            method = proxy.GetMethod("MarkerMethod");
        }

        Assert.IsNotNull(method);
        object[] attrs = method.GetCustomAttributes(false);
        Assert.IsNotNull(attrs, "Should have 1 attribute applied to the target method.");
        Assert.AreEqual(1, attrs.Length, "Should have 1 attribute applied to the target method.");
        Assert.AreEqual(typeof(PublicEnumPropertyAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the target method.");

        PublicEnumPropertyAttribute pepa = attrs[0] as PublicEnumPropertyAttribute;
        Assert.AreEqual(AttributeTargets.All, pepa.Data);
    }

#if !NETCOREAPP
    // http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=161522
    // should not fail
    // Use CustomAttributeData if patch applied (.NET 2.0 SP1).
    // Use Attribute instance if patch not applied (.NET 2.0 SP1).
    [Test]
    public void ProxyWebServiceAttribute()
    {
        IProxyTypeBuilder builder = GetProxyBuilder();
        builder.TargetType = typeof(ClassWithWebServiceAttribute);

        Type proxy = builder.BuildProxyType();
        Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");

        object[] attrs = proxy.GetCustomAttributes(false);
        Assert.IsNotNull(attrs, "Should have had 1 attribute applied to the target type.");
        Assert.AreEqual(1, attrs.Length, "Should have had 1 attribute applied to the target type.");
        Assert.AreEqual(typeof(System.Web.Services.WebServiceAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the target type.");

        var wsa = attrs[0] as System.Web.Services.WebServiceAttribute;
        Assert.AreEqual("blah", wsa.Name);
        Assert.AreEqual("http://mynamespace.com", wsa.Namespace);
    }
#endif

#if !MONO && !NETCOREAPP
    // http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=94803
    [Test]
    [Platform("Win")]
    public void ProxySecurityAttribute()
    {
        IProxyTypeBuilder builder = GetProxyBuilder();
        builder.TargetType = typeof(ClassWithSecurityAttribute);

        Type proxy = builder.BuildProxyType();
        Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");

        object[] attrs = proxy.GetCustomAttributes(false);
        Assert.IsNotNull(attrs, "Should have had 2 attribute applied to the target type.");
        Assert.AreEqual(2, attrs.Length, "Should have had 2 attribute applied to the target type.");
    }
#endif

    [Test]
    public void ProxySpecificTargetTypeAttributeWithArrayConstructor()
    {
        IProxyTypeBuilder builder = GetProxyBuilder();
        builder.TargetType = typeof(ClassWithArrayConstructorAttribute);
        Type proxy = builder.BuildProxyType();
        Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");

        MethodInfo method = proxy.GetMethod("Spring.Proxy.ISomeMarkerInterface.MarkerMethod", BindingFlags.NonPublic | BindingFlags.Instance);
        if (method == null)
        {
            method = proxy.GetMethod("MarkerMethod");
        }

        Assert.IsNotNull(method);
        object[] attrs = method.GetCustomAttributes(false);
        Assert.IsNotNull(attrs, "Should have 1 attribute applied to the target method.");
        Assert.AreEqual(1, attrs.Length, "Should have 1 attribute applied to the target method.");
        Assert.AreEqual(typeof(ArrayConstructorPropertyAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the target method.");

        ArrayConstructorPropertyAttribute acpa = attrs[0] as ArrayConstructorPropertyAttribute;
        Assert.AreEqual(false, acpa.ReadOnly);
        Assert.AreEqual(1, acpa.Types.Length);
        Assert.AreEqual(typeof(string), acpa.Types[0]);
    }

    [Test]
    public void ProxySpecificTargetTypeAttributeWithArrayProperty()
    {
        IProxyTypeBuilder builder = GetProxyBuilder();
        builder.TargetType = typeof(ClassWithArrayPropertyAttribute);
        Type proxy = builder.BuildProxyType();
        Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");

        MethodInfo method = proxy.GetMethod("Spring.Proxy.IAnotherMarkerInterface.MarkerMethod", BindingFlags.NonPublic | BindingFlags.Instance);
        if (method == null)
        {
            method = proxy.GetMethod("MarkerMethod");
        }

        Assert.IsNotNull(method);
        object[] attrs = method.GetCustomAttributes(false);
        Assert.IsNotNull(attrs, "Should have 1 attribute applied to the target method.");
        Assert.AreEqual(1, attrs.Length, "Should have 1 attribute applied to the target method.");
        Assert.AreEqual(typeof(ArrayConstructorPropertyAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the target method.");

        ArrayConstructorPropertyAttribute acpa = attrs[0] as ArrayConstructorPropertyAttribute;
        Assert.AreEqual(true, acpa.ReadOnly);
        Assert.AreEqual(2, acpa.Types.Length);
        Assert.AreEqual(typeof(int), acpa.Types[0]);
        Assert.AreEqual(typeof(string), acpa.Types[1]);
    }

    [Test]
    public void ProxyTargetTypeAttributes()
    {
        IProxyTypeBuilder builder = GetProxyBuilder();
        builder.TargetType = typeof(AnotherMarkerClass);
        Type proxy = builder.BuildProxyType();
        Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");
        object[] attrs = proxy.GetCustomAttributes(false);
        Assert.IsNotNull(attrs, "Should have had 1 attribute applied to the target type.");
        Assert.AreEqual(1, attrs.Length, "Should have had 1 attribute applied to the target type.");
        Assert.AreEqual(typeof(MarkerAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the target type.");
    }

    [Test]
    public void DoesNotProxyTargetTypeAttributesWithProxyTargetAttributesEqualsFalse()
    {
        IProxyTypeBuilder builder = GetProxyBuilder();
        builder.TargetType = typeof(AnotherMarkerClass);
        builder.ProxyTargetAttributes = false;
        Type proxy = builder.BuildProxyType();
        Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");
        object[] attrs = proxy.GetCustomAttributes(false);
        Assert.IsNotNull(attrs);
        Assert.AreEqual(0, attrs.Length, "Should not have attribute applied to the target type.");
    }

    [Test]
    public void ProxyTargetMethodAttributes()
    {
        IProxyTypeBuilder builder = GetProxyBuilder();
        builder.TargetType = typeof(AnotherMarkerClass);
        Type proxy = builder.BuildProxyType();
        Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");
        MethodInfo method = proxy.GetMethod("Spring.Proxy.IAnotherMarkerInterface.MarkerMethod", BindingFlags.NonPublic | BindingFlags.Instance);
        if (method == null)
        {
            method = proxy.GetMethod("MarkerMethod");
        }

        Assert.IsNotNull(method);
        object[] attrs = method.GetCustomAttributes(false);
        Assert.IsNotNull(attrs, "Should have 1 attribute applied to the target method.");
        Assert.AreEqual(1, attrs.Length, "Should have 1 attribute applied to the target method.");
        Assert.AreEqual(typeof(MarkerAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the target method.");
    }

    [Test]
    public void DoesNotProxyTargetMethodAttributesWithProxyTargetAttributesEqualsFalse()
    {
        IProxyTypeBuilder builder = GetProxyBuilder();
        builder.TargetType = typeof(AnotherMarkerClass);
        builder.ProxyTargetAttributes = false;
        Type proxy = builder.BuildProxyType();
        Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");
        MethodInfo method = proxy.GetMethod("Spring.Proxy.IAnotherMarkerInterface.MarkerMethod", BindingFlags.NonPublic | BindingFlags.Instance);
        if (method == null)
        {
            method = proxy.GetMethod("MarkerMethod");
        }

        Assert.IsNotNull(method);
        object[] attrs = method.GetCustomAttributes(false);
        Assert.IsNotNull(attrs);
        Assert.AreEqual(0, attrs.Length, "Should not have attribute applied to the target method.");
    }

    [Test]
    public void ProxyTargetMethodParameterAttributes()
    {
        IProxyTypeBuilder builder = GetProxyBuilder();
        builder.TargetType = typeof(SomeMarkerClass);
        Type proxy = builder.BuildProxyType();
        Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");
        MethodInfo method = proxy.GetMethod("Spring.Proxy.ISomeMarkerInterface.MarkerMethod", BindingFlags.NonPublic | BindingFlags.Instance);
        if (method == null)
        {
            method = proxy.GetMethod("MarkerMethod");
        }

        Assert.IsNotNull(method);
        object[] attrs = method.GetParameters()[1].GetCustomAttributes(false);
        Assert.IsNotNull(attrs, "Should have had 1 attribute applied to the method's parameter.");
        Assert.AreEqual(1, attrs.Length, "Should have had 1 attribute applied to the method's parameter.");
        Assert.AreEqual(typeof(MarkerAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the method's parameter.");
    }

    [Test]
    [Description("SPRNET-1134")]
    public void ProxyTargetMethodParameterMultipleAttributes()
    {
        IProxyTypeBuilder builder = GetProxyBuilder();
        builder.TargetType = typeof(MultipleMarkerClass);
        Type proxy = builder.BuildProxyType();
        Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");
        MethodInfo method = proxy.GetMethod("Spring.Proxy.ISomeMarkerInterface.MarkerMethod", BindingFlags.NonPublic | BindingFlags.Instance);
        if (method == null)
        {
            method = proxy.GetMethod("MarkerMethod");
        }

        Assert.IsNotNull(method);
        object[] attrs = method.GetParameters()[1].GetCustomAttributes(false);
        Assert.IsNotNull(attrs, "Should have had 2 attributes applied to the method's parameter.");
        Assert.AreEqual(2, attrs.Length, "Should have had 2 attributes applied to the method's parameter.");
        Assert.AreEqual(typeof(MarkerAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the method's parameter.");
        Assert.AreEqual(typeof(MarkerAttribute), attrs[1].GetType(), "Wrong System.Type of Attribute applied to the method's parameter.");
    }

    [Test]
    public void DoesNotProxyTargetMethodParameterAttributesWithProxyTargetAttributesEqualsFalse()
    {
        IProxyTypeBuilder builder = GetProxyBuilder();
        builder.TargetType = typeof(SomeMarkerClass);
        builder.ProxyTargetAttributes = false;
        Type proxy = builder.BuildProxyType();
        Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");
        MethodInfo method = proxy.GetMethod("Spring.Proxy.ISomeMarkerInterface.MarkerMethod", BindingFlags.NonPublic | BindingFlags.Instance);
        if (method == null)
        {
            method = proxy.GetMethod("MarkerMethod");
        }

        Assert.IsNotNull(method);
        object[] attrs = method.GetParameters()[1].GetCustomAttributes(false);
        Assert.IsNotNull(attrs);
        Assert.AreEqual(0, attrs.Length, "Should not have attribute applied to the method's parameter.");
    }

    [Test]
    public void ProxyTargetMethodReturnValueAttributes()
    {
        IProxyTypeBuilder builder = GetProxyBuilder();
        builder.TargetType = typeof(SomeMarkerClass);
        Type proxy = builder.BuildProxyType();
        Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");
        MethodInfo method = proxy.GetMethod("Spring.Proxy.ISomeMarkerInterface.MarkerMethod", BindingFlags.NonPublic | BindingFlags.Instance);
        if (method == null)
        {
            method = proxy.GetMethod("MarkerMethod");
        }

        Assert.IsNotNull(method);
        object[] attrs = method.ReturnTypeCustomAttributes.GetCustomAttributes(false);
        Assert.IsNotNull(attrs, "Should have had 1 attribute applied to the method's return value.");
        Assert.AreEqual(1, attrs.Length, "Should have had 1 attribute applied to the method's return value.");
        Assert.AreEqual(typeof(MarkerAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the method's return value.");
    }

    [Test]
    [Description("SPRNET-1134")]
    public void ProxyTargetMethodReturnValueMultipleAttributes()
    {
        IProxyTypeBuilder builder = GetProxyBuilder();
        builder.TargetType = typeof(MultipleMarkerClass);
        Type proxy = builder.BuildProxyType();
        Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");
        MethodInfo method = proxy.GetMethod("Spring.Proxy.ISomeMarkerInterface.MarkerMethod", BindingFlags.NonPublic | BindingFlags.Instance);
        if (method == null)
        {
            method = proxy.GetMethod("MarkerMethod");
        }

        Assert.IsNotNull(method);
        object[] attrs = method.ReturnTypeCustomAttributes.GetCustomAttributes(false);
        Assert.IsNotNull(attrs, "Should have had 2 attribute applied to the method's return value.");
        Assert.AreEqual(2, attrs.Length, "Should have had 2 attribute applied to the method's return value.");
        Assert.AreEqual(typeof(MarkerAttribute), attrs[0].GetType(), "Wrong System.Type of Attribute applied to the method's return value.");
        Assert.AreEqual(typeof(MarkerAttribute), attrs[1].GetType(), "Wrong System.Type of Attribute applied to the method's return value.");
    }

    [Test]
    public void DoesNotProxyTargetMethodReturnValueAttributesWithProxyTargetAttributesEqualsFalse()
    {
        IProxyTypeBuilder builder = GetProxyBuilder();
        builder.TargetType = typeof(SomeMarkerClass);
        builder.ProxyTargetAttributes = false;
        Type proxy = builder.BuildProxyType();
        Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");
        MethodInfo method = proxy.GetMethod("Spring.Proxy.ISomeMarkerInterface.MarkerMethod", BindingFlags.NonPublic | BindingFlags.Instance);
        if (method == null)
        {
            method = proxy.GetMethod("MarkerMethod");
        }

        Assert.IsNotNull(method);
        object[] attrs = method.ReturnTypeCustomAttributes.GetCustomAttributes(false);
        Assert.IsNotNull(attrs);
        Assert.AreEqual(0, attrs.Length, "Should not have attribute applied to the method's return value.");
    }

    [Test]
    public void ProxyGenericMethod()
    {
        IProxyTypeBuilder builder = GetProxyBuilder();
        builder.TargetType = typeof(ClassWithGenericMethod);

        Type proxy = builder.BuildProxyType();
        Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");

        InterfaceWithGenericMethod foo = Activator.CreateInstance(proxy) as InterfaceWithGenericMethod;
        Assert.IsNotNull(foo);

        foo.PolymorphicMethod<int>();

        string result1 = foo.PolymorphicMethod("coucou");
        Assert.AreEqual("coucou", result1);

        foo.WithGenericParameter<string>();

        foo.WithGenericParameterAndGenericArgument<string>("ola");

        string result2 = foo.WithGenericParameterAndGenericArgumentAndGenericReturnType<string>("ola");
        Assert.AreEqual("ola", result2);

        foo.WithInterfaceConstraint<bool>();

        foo.WithBaseTypeConstraint<DerivedTestObject>();

        foo.WithBaseTypeAndInterfaceConstraints<DerivedTestObject>();

        foo.WithMixedConstraint<bool, DerivedTestObject>();
    }

    public interface InterfaceWithGenericMethod
    {
        void PolymorphicMethod<T>();

        string PolymorphicMethod(string param);

        void WithGenericParameter<T>();

        void WithGenericParameterAndGenericArgument<T>(T obj);

        T WithGenericParameterAndGenericArgumentAndGenericReturnType<T>(T obj);

        void WithInterfaceConstraint<Z>() where Z : IComparable;

        void WithBaseTypeConstraint<Z>() where Z : TestObject;

        void WithBaseTypeAndInterfaceConstraints<Z>() where Z : TestObject, IComparable, IDisposable;

        void WithMixedConstraint<X, Y>()
            where X : IComparable, new()
            where Y : TestObject;
    }

    public class ClassWithGenericMethod : InterfaceWithGenericMethod
    {
        public void PolymorphicMethod<T>() { }

        public string PolymorphicMethod(string param)
        {
            return param;
        }

        public void WithGenericParameter<T>() { }

        public void WithGenericParameterAndGenericArgument<T>(T obj) { }

        public T WithGenericParameterAndGenericArgumentAndGenericReturnType<T>(T obj)
        {
            return obj;
        }

        public void WithInterfaceConstraint<Z>() where Z : IComparable { }

        public void WithBaseTypeConstraint<Z>() where Z : TestObject { }

        public void WithBaseTypeAndInterfaceConstraints<Z>() where Z : TestObject, IComparable, IDisposable { }

        public void WithMixedConstraint<X, Y>()
            where X : IComparable, new()
            where Y : TestObject
        {
        }
    }

    [Test]
    public void ProxyGenericInterface()
    {
        IProxyTypeBuilder builder = GetProxyBuilder();
        builder.TargetType = typeof(ClassThatImplementsGenericInterface<TestObject>);

        Type proxy = builder.BuildProxyType();
        Assert.IsNotNull(proxy, "The proxy generated by a (valid) call to BuildProxy() was null.");

        GenericInterface<TestObject> foo = Activator.CreateInstance(proxy) as GenericInterface<TestObject>;
        Assert.IsNotNull(foo);

        TestObject to1 = foo.Create();
        Assert.IsNotNull(to1);

        TestObject to2 = foo.Populate(new TestObject());
        Assert.IsNotNull(to2);
        Assert.AreEqual("Populated", to2.Name);
    }

    public interface GenericInterface<T>
        where T : ITestObject, new()
    {
        T Create();

        T Populate(T testObject);
    }

    public class ClassThatImplementsGenericInterface<T> : GenericInterface<T>
        where T : ITestObject, new()
    {
        public T Create()
        {
            return new T();
        }

        public T Populate(T testObject)
        {
            testObject.Name = "Populated";

            return testObject;
        }
    }

    protected abstract IProxyTypeBuilder GetProxyBuilder();

    public interface InnerInterface
    {
    }

    public class InnerClass : InnerInterface
    {
    }
}

public class DoesntImplementAnyInterfaces
{
}

public interface IMarkerInterface
{
}

public class MarkerClass : IMarkerInterface
{
}

[GuidAttribute("7cfc9607-e81a-48ac-a080-bda88193607b")]
public class ClassWithGuidAttribute : IMarkerInterface
{
}

[Marker]
public interface IAnotherMarkerInterface
{
    [Marker]
    void MarkerMethod();
}

public interface ISomeMarkerInterface
{
    string MarkerMethod(int param1, string param2);
}

// XmlElementAttribute.Order property is only available in .NET 2.0
public sealed class FakeXmlElementAttribute : Attribute
{
    private int _order;

    public int Order
    {
        get
        {
            return this._order;
        }
        set
        {
            if (value < 0)
            {
                throw new ArgumentException("Negative values are prohibited.", "Order");
            }

            this._order = value;
        }
    }

    private string _elementName;

    public string ElementName
    {
        get { return _elementName; }
        set { _elementName = value; }
    }

    public FakeXmlElementAttribute()
    {
        this._order = -1;
    }

    public FakeXmlElementAttribute(string elementName)
    {
        this._order = -1;
        this._elementName = elementName;
    }
}

public sealed class FakeGenerateScriptTypeAttribute : Attribute
{
    public FakeGenerateScriptTypeAttribute(string name)
    {
        if (name == null)
        {
            throw new ArgumentNullException("name");
        }

        this._name = name;
    }

    private string _name;

    public string Name
    {
        get
        {
            return this._name;
        }
    }
}

public sealed class FakeServiceKnownTypeAttribute : Attribute
{
    private Type declaringType;
    private string methodName;
    private Type type;

    private FakeServiceKnownTypeAttribute()
    {
    }

    public FakeServiceKnownTypeAttribute(string methodName)
    {
        this.methodName = methodName;
    }

    public FakeServiceKnownTypeAttribute(Type type)
    {
        this.type = type;
    }

    public FakeServiceKnownTypeAttribute(string methodName, Type declaringType)
    {
        this.methodName = methodName;
        this.declaringType = declaringType;
    }

    public Type DeclaringType
    {
        get
        {
            return this.declaringType;
        }
    }

    public string MethodName
    {
        get
        {
            return this.methodName;
        }
    }

    public Type Type
    {
        get
        {
            return this.type;
        }
    }
}

public class ClassWithFakeXmlElementAttribute : ISomeMarkerInterface
{
    public string MarkerMethod([FakeXmlElement("parameter1")] int param1, string param2)
    {
        return null;
    }
}

#if !NETCOREAPP
// http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=161522
[System.Web.Services.WebService(Namespace = "http://mynamespace.com", Name = "blah")]
public class ClassWithWebServiceAttribute : IMarkerInterface
{
}
#endif

[FakeGenerateScriptType("ScriptName")]
public class ClassWithFakeGenerateScriptTypeAttribute : IMarkerInterface
{
}

//[FakeServiceKnownTypeAttribute(typeof(TestObject))]
public class ClassWithFakeServiceKnownTypeAttribute : IMarkerInterface
{
}

// http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=296032
public class ClassWithPublicEnumPropertyAttribute : ISomeMarkerInterface
{
    [PublicEnumProperty(AttributeTargets.All)]
    public string MarkerMethod(int param1, string param2)
    {
        return null;
    }
}

#if !NETCOREAPP
// http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=94803
[Marker]
#pragma warning disable 618
[System.Net.WebPermission(System.Security.Permissions.SecurityAction.Deny)]
#pragma warning restore 618
public class ClassWithSecurityAttribute : IMarkerInterface
{
}
#endif

public class ClassWithArrayConstructorAttribute : ISomeMarkerInterface
{
    [ArrayConstructorPropertyAttribute(false, new Type[1] { typeof(string) })]
    public string MarkerMethod(int param1, string param2)
    {
        return null;
    }
}

public class ClassWithArrayPropertyAttribute : IAnotherMarkerInterface
{
    [ArrayConstructorPropertyAttribute(ReadOnly = true, Types = new Type[2] { typeof(int), typeof(string) })]
    public void MarkerMethod()
    {
    }
}

public class SomeMarkerClass : ISomeMarkerInterface
{
    [return: Marker]
    public string MarkerMethod(int param1, [Marker] string param2)
    {
        return string.Empty;
    }

    [return: Marker]
    public virtual string MarkerVirtualMethod(int param1, [Marker] string param2)
    {
        return string.Empty;
    }
}

// SPRNET-1134
public class MultipleMarkerClass : ISomeMarkerInterface
{
    [return: Marker(Count = 0)]
    [return: Marker(Count = 1)]
    public string MarkerMethod(int param1, [Marker(Count = 0)] [Marker(Count = 1)] string param2)
    {
        return string.Empty;
    }

    [return: Marker(Count = 0)]
    [return: Marker(Count = 1)]
    public virtual string MarkerVirtualMethod(int param1, [Marker(Count = 0)] [Marker(Count = 1)] string param2)
    {
        return string.Empty;
    }
}

[Marker]
public class AnotherMarkerClass : IAnotherMarkerInterface
{
    [Marker]
    public void MarkerMethod()
    {
    }

    [Marker]
    public virtual void MarkerVirtualMethod()
    {
    }
}

public class ArrayConstructorPropertyAttribute : Attribute
{
    private bool _readOnly = false;
    private Type[] _types = Type.EmptyTypes;

    public ArrayConstructorPropertyAttribute()
    {
    }

    public ArrayConstructorPropertyAttribute(bool readOnly, Type[] types)
    {
        this._readOnly = readOnly;
        this._types = types;
    }

    public bool ReadOnly
    {
        get { return _readOnly; }
        set { _readOnly = value; }
    }

    public Type[] Types
    {
        get { return _types; }
        set { _types = value; }
    }
}

public class PublicEnumPropertyAttribute : Attribute
{
    private Enum _data;

    public PublicEnumPropertyAttribute(AttributeTargets data)
    {
        _data = data;
    }

    public PublicEnumPropertyAttribute(TypeCode data)
    {
        _data = data;
    }

    public Enum Data
    {
        get { return _data; }
    }
}

[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
public sealed class MarkerAttribute : Attribute
{
    private int _count;

    public int Count
    {
        get { return _count; }
        set { _count = value; }
    }
}

public interface IBase
{
    void Base();
}

public interface IInherited : IBase
{
    void Inherited();
}

public class MultipleInterfaces : IInherited
{
    public void Inherited()
    {
    }

    public void Base()
    {
    }
}

public interface ITargetObjectTest
{
    string InterfaceProperty { get; set; }

    bool InterfaceMethodWithArguments(string code);

    void InterfaceVirtualMethodWithNoArguments();
}

public class TargetObjectTest : ITargetObjectTest
{
    private string _interfaceProperty;

    public TargetObjectTest() { }

    public TargetObjectTest(string interfaceProperty)
    {
        _interfaceProperty = interfaceProperty;
    }

    public virtual string InterfaceProperty
    {
        get { return _interfaceProperty; }
        set { _interfaceProperty = value; }
    }

    public bool InterfaceMethodWithArguments(string code)
    {
        return true;
    }

    public virtual void InterfaceVirtualMethodWithNoArguments()
    {
    }

    public void MethodWithArguments(string code)
    {
    }
}

[ProxyIgnore]
public interface IFrameworkInterface
{
    void FrameworkMethod();
}

public interface IApplicationInterface
{
    void ApplicationMethod();
}

public class ApplicationClass : IApplicationInterface, IFrameworkInterface
{
    public void FrameworkMethod() { }

    public void ApplicationMethod() { }
}
